programming4us
           
 
 
Programming

iPad Development : Document Management (part 1)

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
11/20/2010 3:56:28 PM
The changes we made in the previous section gave us the basics for saving and loading a single file, but we'll need more than that! The iOS doesn't have anything like the Finder, so the only access our users will have to the drawings they make is what we provide for them in our app. That means we should offer at least the following capabilities:
  • See a list of all Dudel documents

  • Create a new document

  • Rename a document

  • Delete a document

  • Keep track of which document was last used

Rather than putting the code for all that into one of our existing controller classes, we'll make a new class to manage the Dudel documents for us. This class will provide a single shared instance for all our other classes to use, and anyone who needs to access documents in any way will go through that shared instance.

1. Listing Files

Make a new class (a direct subclass of NSObject) and name it FileList. Here's the interface for FileList.h, which includes a notification name used to tell interested parties that the list of files has changed, properties for reading the list of all available documents and accessing the current document, and methods to do the other document-management operations:

//  FileList.h

#import <Foundation/Foundation.h>

// notification name
#define FileListChanged @"FileListChanged"

@interface FileList : NSObject {
NSMutableArray *allFiles;
NSString *currentFile;
}

@property (nonatomic, readonly) NSArray *allFiles;
@property (nonatomic, copy) NSString *currentFile;

+ (FileList *)sharedFileList;

- (void)deleteCurrentFile;
- (void)renameFile:(NSString *)oldFilename to:(NSString *)newFilename;
- (void)renameCurrentFile:(NSString *)newFilename;
- (NSString *)createAndSelectNewUntitled;

@end

As for the implementation, FileList uses NSFileManager to do file operations, and NSUserDefaults to keep track of the current file. This is all fairly standard Objective-C activity.

//  FileList.m
#import "FileList.h"
#import "SynthesizeSingleton.h"

// key for storing current filename in user defaults
#define DEFAULT_FILENAME_KEY @"defaultFilenameKey"

@implementation FileList
@synthesize allFiles;
@synthesize currentFile;

SYNTHESIZE_SINGLETON_FOR_CLASS(FileList)


- init {
if (self = [super init]) {
NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *dirPath = [dirs objectAtIndex:0];
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath
error:NULL];
NSArray *sortedFiles = [[files pathsMatchingExtensions:[NSArray
arrayWithObject:@"dudeldoc"]] sortedArrayUsingSelector:@selector(compare:)];
allFiles = [[NSMutableArray array] retain];
// the filenames returned by pathsMatchingExtensions: don't include the whole
// file path, so we add it to each file here.
for (NSString *file in sortedFiles) {
[allFiles addObject:[dirPath stringByAppendingPathComponent:file]];
}
currentFile = [[[NSUserDefaults standardUserDefaults]
stringForKey:DEFAULT_FILENAME_KEY] retain];
if ([allFiles count]==0) {
// there are no documents, make one!
[self createAndSelectNewUntitled];
} else if (![allFiles containsObject:currentFile]) {
// user defaults are suggesting a file that doesn't exist in our documents
// directory, so just use the first file in the list.
self.currentFile = [allFiles objectAtIndex:0];
}
}
return self;
}
- (void)setCurrentFile:(NSString *)filename {
if (![currentFile isEqual:filename]) {
[currentFile release];
currentFile = [filename copy];
[[NSUserDefaults standardUserDefaults] setObject:currentFile
forKey:DEFAULT_FILENAME_KEY];
[[NSNotificationCenter defaultCenter] postNotificationName:FileListChanged
object:self];
}
}
- (void)deleteCurrentFile {
if (self.currentFile) {
NSUInteger filenameIndex = [self.allFiles indexOfObject:self.currentFile];
NSError *error = nil;
BOOL result = [[NSFileManager defaultManager] removeItemAtPath:self.currentFile
error:&error];

if (filenameIndex != NSNotFound) {
[allFiles removeObjectAtIndex:filenameIndex];
// now figure out which file to make current
if ([self.allFiles count]==0) {
[self createAndSelectNewUntitled];
} else {
if ([self.allFiles count]==filenameIndex) {
filenameIndex--;
}
self.currentFile = [self.allFiles objectAtIndex:filenameIndex];
}



}
[[NSNotificationCenter defaultCenter] postNotificationName:FileListChanged
object:self];
}
}
- (void)renameFile:(NSString *)oldFilename to:(NSString *)newFilename {
[[NSFileManager defaultManager] moveItemAtPath:oldFilename toPath:newFilename
error:NULL];
if ([self.currentFile isEqual:oldFilename]) {
self.currentFile = newFilename;
}
int nameIndex = [self.allFiles indexOfObject:oldFilename];
if (nameIndex != NSNotFound) {
[allFiles replaceObjectAtIndex:nameIndex withObject:newFilename];
}
[[NSNotificationCenter defaultCenter] postNotificationName:FileListChanged
object:self];
}
- (void)renameCurrentFile:(NSString *)newFilename {
[self renameFile:self.currentFile to:newFilename];
}
- (NSString *)createAndSelectNewUntitled {
NSString *defaultFilename = [NSString stringWithFormat:@"Dudel %@.dudeldoc",
[NSDate date]];
NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *filename = [[dirs objectAtIndex:0] stringByAppendingPathComponent:
defaultFilename];
[[NSFileManager defaultManager] createFileAtPath:filename contents:nil
attributes:nil];
[allFiles addObject:filename];
[allFiles sortUsingSelector:@selector(compare:)];
self.currentFile = filename;
[[NSNotificationCenter defaultCenter] postNotificationName:FileListChanged
object:self];
return self.currentFile;
}
@end

1.1. Adding a File List Controller

The FileList class will be used by both DudelViewController and our application delegate, as well as one other class we haven't yet created: FileListViewController, which will display a list of all the files and let the user select a file to change the current selection. Create a new UIViewController subclass, and click the check boxes to make it iPad-ready and a subclass of UITableViewController (but no .xib file). Then name it FileListViewController. This is going to be a quite standard controller for a table view, using FileList to see what it should be displaying. Here's the code for both the .h and .m files (by now, the structure of a table view controller should look familiar to you):

//  FileListController.h
#import <UIKit/UIKit.h>

// notification name
#define FileListControllerSelectedFile @"FileListControllerSelectedFile"
#define FileListControllerFilename @"FileListControllerFilename"

@interface FileListViewController : UITableViewController {
NSString *currentDocumentFilename;
NSArray *documents;
}
@property (nonatomic, copy) NSString *currentDocumentFilename;
@property (nonatomic, retain) NSArray *documents;
@end

// FileListController.m
#import "FileListViewController.h"
#import "FileList.h"

@implementation FileListViewController
@synthesize currentDocumentFilename, documents;
- (void)reloadData {
self.currentDocumentFilename = [FileList sharedFileList].currentFile;
self.documents = [FileList sharedFileList].allFiles;
[self.tableView reloadData];
}
- (void)fileListChanged:(NSNotification *)n {
[self reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(fileListChanged:) name:FileListChanged
object:[FileList sharedFileList]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self reloadData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)s {
return [self.documents count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
NSString *file = [self.documents objectAtIndex:indexPath.row];
cell.textLabel.text = [[file lastPathComponent] stringByDeletingPathExtension];
if ([file isEqual:self.currentDocumentFilename]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {

cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[documents
objectAtIndex:indexPath.row] forKey:FileListControllerFilename];
[[NSNotificationCenter defaultCenter]
postNotificationName:FileListControllerSelectedFile object:self userInfo:userInfo];
[self reloadData];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.currentDocumentFilename = nil;
self.documents = nil;
[super dealloc];
}
@end

The only interesting thing this class does is register for FileList's notification about changes, so that the view can be updated automatically. Also, whenever the user selects a row here, FileListViewController posts a notification to that effect. We'll use that later in this chapter, to be able to update our main DudelViewControllerDudelView whenever that happens. and
1.2. Changing the App Delegate

For the first time since we started on Dudel, it's time to make some changes to the app delegate. Changes are required here because we're going to rearrange the top-level view arrangement of our application. Inside the .xib file, we'll be making a UISplitViewController the root view controller, with instances of FileListViewController and DudelViewController as its "children" (up until now, DudelViewController was the root view controller).

Before we edit the .xib file, let's make the necessary preparations to DudelAppController.h (the app delegate), basically just adding a couple of outlets:

//  DudelAppDelegate.h
#import <UIKit/UIKit.h>
@class DudelViewController;
@class FileListViewController;
@interface DudelAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
DudelViewController *viewController;
FileListViewController *fileListController;
UISplitViewController *splitViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet DudelViewController *viewController;
@property (nonatomic, retain) IBOutlet FileListViewController *fileListController;
@property (nonatomic, retain) IBOutlet UISplitViewController *splitViewController;
@end

Now open MainWindow.xib in Interface Builder. By default, the main .xib window shows you only the top-level structure of the items contained in the .xib, but we're going to need to fix the plumbing here a bit. First, switch to the column view by clicking the appropriate button, as shown in Figure 1.
Figure 1. The default contents of the MainWindow.xib file created with your project

We're going to add a UISplitViewController, move the DudelViewController into it, and then add a FileListController to the mix. Start by finding a UISplitViewController in the Library and dragging it to the first column shown in the main window. Then click the new split view controller to see what's inside it, and click the navigation controller in there to see what it contains. You should see something like Figure 2.

Figure 2. We've put a split view in place. Now we just need to give it the correct contents.

In the second column, the item labeled View Controller is where we want to have our DudelViewController now. And we want our FileListViewController to be in the item labeled Table View Controller in the third column.

This window does resemble a Finder window, so you might think you could just drag the Dudel view controller already at the top level into the split view controller, but that won't work. Instead, delete the top-level Dudel View Controller item, then click the Split View Controller item and select the view controller it contains. Open the identity inspector, and change its class to DudelViewController. Then click the Navigation Controller item, select the Table View Controller item it contains, and change its class to FileListViewController. You should now see something like Figure 3.

Figure 3. A look at the completed reorganization

Now all that's left here is to make some connections between objects in the .xib file. Connect each of DudelAppDelegate's outlets to the appropriate view controllers in the nib file: splitViewController, fileListController, and viewController. Previously, the viewController outlet was connected to the old DudelViewController, but since we deleted that and are using a new one instead, you'll need to reconnect that outlet to the new DudelViewController inside the split view. Also, connect the UISplitViewController's delegate outlet to the DudelViewController. This seems tricky, since the latter is contained inside the former, but as long as you're in column view, you shouldn't have a problem.

Save your work, and go back to Xcode, where it's time to finish the changes required for DudelAppDelegate.m. We're doing two main things here: switching out references to the top-level view controller and observing a notification from the FileListViewController class, so that whenever the user selects a file, we can set up the DudelViewController with the contents of the newly selected file.

//  DudelAppDelegate.m
#import "DudelAppDelegate.h"
#import "DudelViewController.h"


#import "FileListViewController.h"
#import "FileList.h"
@implementation DudelAppDelegate
@synthesize window;
@synthesize viewController;
@synthesize fileListController;
@synthesize splitViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(fileListControllerSelectedFile:)
name:FileListControllerSelectedFile object:fileListController];
return YES;
}
- (void)fileListControllerSelectedFile:(NSNotification *)n {
NSString *oldFilename = [FileList sharedFileList].currentFile;
[viewController saveCurrentToFile:oldFilename];
NSString *filename = [[n userInfo] objectForKey:FileListControllerFilename];
[FileList sharedFileList].currentFile = filename;
[viewController loadFromFile:filename];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[viewController release];
[splitViewController release];
[window release];
[super dealloc];
}
@end


With that in place, we're getting very close to having a working split view up and running. Hang tight! The next step is to modify DudelViewController.m, removing the temporary "hack" we put in place for loading and saving a file to a single, hard-coded location. Start by importing the header for FileList somewhere at the top of the file:

#import "FileList.h"

Then, in both viewDidLoad and applicationWillTerminate:, make the following change. This eliminates the lengthy path construction, and instead just asks FileList for the current file.

NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *filename = [[dirs objectAtIndex:0]
stringByAppendingPathComponent:@"Untitled.dudeldoc"];

NSString *filename = [FileList sharedFileList].currentFile;

We also need to add an outlet from DudelViewController to the toolbar in its display, so that we can add and remove toolbar items as the iPad rotates. Open DudelViewController.h and add the following line:

IBOutlet UIToolbar *toolbar;

To connect it, open DudelViewController.xib in Interface Builder, double-click to open the DudelView if it's not already open, then control-drag from the File's Owner icon to the toolbar at the bottom of the DudelView, and select toolbar from the pop-up menu.

One final step is necessary for the split view controller. I mentioned earlier that the actual split view is shown only in landscape mode, and that when switching to portrait mode, we instead get a UIBarButtonItem (which is set up to open a popover) passed to the UISplitViewController's delegate (our DudelViewController instance). Likewise, another delegate method is called when switching to landscape mode, telling us that the UIBarButtonItem we were passed earlier is no longer valid. We'll implement these two methods, so that in the first method, we add the item to our toolbar, along with a flexible spacer so that it stands slightly removed from the tools. In the second method, we remove the two items.

- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc {
// insert the new item and a spacer into the 33
NSMutableArray *newItems = [[toolbar.items mutableCopy] autorelease];
[newItems insertObject:barButtonItem atIndex:0];
UIBarButtonItem *spacer = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil
action:nil] autorelease];
[newItems insertObject:spacer atIndex:1];
[toolbar setItems:newItems animated:YES];
// configure display of the button
barButtonItem.title = @"My Dudels";
}
- (void)splitViewController:(UISplitViewController*)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)button {
// remove the button, and the spacer that is beside it
NSMutableArray *newItems = [[toolbar.items mutableCopy] autorelease];
if ([newItems containsObject:button]) {
[newItems removeObject:button];
[newItems removeObjectAtIndex:0];
[toolbar setItems:newItems animated:YES];
}
}

We also need to implement a third delegate method, which is called when the popover created by the UISplitViewController is about to be displayed:
- (void)splitViewController:(UISplitViewController*)svc
popoverController:(UIPopoverController*)pc
willPresentViewController:(UIViewController *)aViewController {
// we don't create this popover on our own, but we want to notice it so that
// we can dismiss any other popovers, and also remove it later.
if (self.currentPopover) {
[self.currentPopover dismissPopoverAnimated:YES];
[self handleDismissedPopoverController:self.currentPopover];
}
self.currentPopover = pc;
}

The point of this is mainly just to make sure that we're not showing multiple popovers, as we've done with all the other popovers.

At this point, you should now be able to build and run your app, and—finally!—see the split view in action. Rotate to landscape mode, and the file list appears on the left. Rotate to portrait mode, and the file list disappears, but in its place, there's a button at the left edge of the toolbar that brings up the file list in a popover.

That's great, but there's still a bit of a problem. The file list has only one item, which you can't rename or delete, and there's no way to make a new item! To remedy this, we need to add a few more things.


Other -----------------
- iPad Development : The Split View Concept
- jQuery 1.3 : Developing plugins - Adding new shortcut methods
- jQuery 1.3 : Developing plugins - DOM traversal methods
- Using Cloud Services : Exploring Online Planning and Task Management
- Using Cloud Services : Exploring Online Scheduling Applications
- Using Cloud Services : Exploring Online Calendar Applications
- SOA with .NET and Windows Azure : Service Contracts with WCF (part 3)
- SOA with .NET and Windows Azure : Service Contracts with WCF (part 2)
- SOA with .NET and Windows Azure : Service Contracts with WCF (part 1)
- Cloud Security and Privacy : Data Security and Storage
- iPad SDK : Working with Documents - Desktop Synchronization
- Required Project Images for iPad Apps
- iPhone SDK : GameKit Voice Chat
- iPhone SDK : Creating Basic GameKit Services (part 2) : Sending and Receiving Data
- iPhone SDK : Creating Basic GameKit Services (part 1)
- iPad : Navigating with Maps
- Adding iPad to the Mix
- A Brief History of Legacy .NET Distributed Technologies : .NET Remoting
- A Brief History of Legacy .NET Distributed Technologies : .NET Enterprise Services
- iPad SDK : Outputting to an External Screen
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us